package net.hmr_radio.hmrbot.service;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityExistsException;

import net.hmr_radio.hmrbot.exception.AuthenticationException;
import net.hmr_radio.hmrbot.meta.BotMeta;
import net.hmr_radio.hmrbot.meta.RepliedMeta;
import net.hmr_radio.hmrbot.model.Bot;
import net.hmr_radio.hmrbot.model.Replied;

import org.slim3.datastore.Datastore;
import org.slim3.datastore.EntityNotFoundRuntimeException;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.auth.AccessToken;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query.FilterOperator;

public class BotService {

    private BotMeta meta = BotMeta.get();
    private RepliedMeta rep = RepliedMeta.get();

    /**
     * 新規Botエンティティを作成.
     * 
     * twitterのアクセストークン情報、screen_nameを使用してBotのデータを作る.
     * すでに同じscreen_nameを持つBotが存在する場合はエラー.
     * 
     * @param twitter
     *            -
     * @throws TwitterException
     *             -
     * @throws EntityExistsException
     *             -
     */
    public void createNewBot(Twitter twitter) throws TwitterException,
            EntityExistsException {
        if (get(twitter) != null) {
            // すでに存在する
            throw new EntityExistsException("bot["
                + twitter.getScreenName()
                + "] is already exists");
        }

        Bot bot = new Bot();
        bot.setTwitterId(twitter.getScreenName());
        bot.setOwnerId(twitter.getScreenName());
        bot.setName(twitter.getScreenName());
        AccessToken acessToken = twitter.getOAuthAccessToken();
        bot.setAccessToken(acessToken.getToken());
        bot.setAccessTokenSecret(acessToken.getTokenSecret());

        save(bot);
    }

    public void save(Bot bot) {
        if (bot.getKey() == null) {
            bot.setKey(bot.createKey());
        }
        Datastore.put(bot);
    }

    /**
     * keyでBotを検索.
     * 
     * 存在しない場合はnullを返す.
     * 
     * @param key
     *            -
     * @return -
     */
    public Bot get(Key key) {
        return Datastore.getOrNull(Bot.class, key);
    }

    /**
     * screenNameでBotを検索.
     * 
     * 存在しない場合はnullを返す.
     * 
     * @param key
     *            -
     * @return -
     */
    public Bot get(String screenName) {
        return get(Datastore.createKey(Bot.class, screenName));
    }

    /**
     * twitterでBotを検索.
     * 
     * 存在しない場合はnullを返す.
     * 
     * @param key
     *            -
     * @return -
     */
    public Bot get(Twitter twitter) throws IllegalStateException,
            TwitterException {
        return get(twitter.getScreenName());
    }

    public List<Bot> findByUserId(String userId) {
        return Datastore
            .query(Bot.class)
            .filter(meta.ownerId.getName(), FilterOperator.EQUAL, userId)
            .sort(meta.key.asc.getSort())
            .asList();
    }

    /**
     * Botを削除する.
     * 
     * 削除者がBotのオーナーではない場合はエラー.
     * 
     * @param key
     *            -
     * @param userId
     *            -
     */
    public void delete(Key key, String userId) throws NullPointerException,
            AuthenticationException, EntityNotFoundRuntimeException {
        if (key == null || userId == null) {
            throw new NullPointerException("parameter is null");
        }
        Bot bot = Datastore.get(Bot.class, key);
        if (!userId.equals(bot.getOwnerId())) {
            throw new AuthenticationException();
        }
        Datastore.delete(bot.getKey());
    }

    public List<Bot> getRunBots() {
        Date ma; // １時間前
        {
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.HOUR, -1);
            ma = cal.getTime();
        }
        return Datastore
            .query(Bot.class)
            .filter(meta.stop.equal(Boolean.FALSE), meta.lastRun.lessThan(ma))
            .sort(meta.lastRun.desc.getSort())
            .limit(3)
            .asList();
    }

    public boolean isReplied(String screenName, long statusId) {
        return Datastore
            .query(Replied.class)
            .filter(
                rep.botScreenName.equal(screenName),
                rep.repliedStatudId.equal(statusId))
            .count() > 0;
    }
}
